﻿
namespace Patterns4Net.Tests.Core.CecilExtensions
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Mono.Cecil;
    using Mono.Cecil.Rocks;
    using NUnit.Framework;
    using Patterns4Net.Core;
    using Patterns4Net.Core.CecilExtensions;

    [TestFixture]
    public class TypeExtensionsTest
    {
        [Test]
        public void GetAllMethods_For_Baz_Returns_All_Methods_From_InheritanceJungle()
        {
            var methods = GetTypeDefinition<Baz>().GetAllMethods();
            CollectionAssert.AreEquivalent(
                new[] { "VirtualM", "Moo", "System.ICloneable.Clone", "CompareTo", "ToBeSealed", "OverriddenM", "AbstractM" },
                methods.Select(m => m.Name));
        }

        [Test]
        public void GetOverrideableMethods_For_Baz_Returns_VirtualM_AbstractM_And_OverriddeM()
        {
            var methods = GetTypeDefinition<Baz>().GetOverrideableMethods();
            var names = methods.Select(m => m.Name);
            CollectionAssert.AreEquivalent(
                new[] { "VirtualM", "AbstractM", "OverriddenM", "Equals", "ToString", "GetHashCode", "Finalize" },
                names,
                string.Format("Result: {0}", string.Join(",", names)));
        }

        [Test]
        public void Overrides_Returns_True_For_Bar_And_ToString()
        {
            var result = GetTypeDefinition<Bar>().Overrides(GetTypeDefinition<object>().GetMethod("ToString"));
            Assert.IsFalse(result);
        }

        [Test]
        public void Overrides_Returns_True_For_Baz_And_VirtualM()
        {
            var result = GetTypeDefinition<Baz>().Overrides(GetTypeDefinition<Bar>().GetMethod("VirtualM"));
            Assert.IsTrue(result);
        }

        [Test]
        public void GetBaseTypes_For_Baz_Returns_Foo_Bar_And_Object()
        {
            var names = GetTypeDefinition<Baz>().GetBaseTypes().Select(m => m.Name).ToArray();
            CollectionAssert.AreEquivalent(
                new[] { "Foo", "Bar", "Object" }, 
                names,
                string.Format("Result: {0}", string.Join(",", names)));
        }

        [Test]
        public void IsGenericInstanceOf_Returns_True_For_IEnumerableInt_Generic_IEnumerable_And_Int()
        {
            var genericEnumerable = typeof(IEnumerable<>).GetTypeDefinition();
            var intEnumerable = genericEnumerable.MakeGenericInstanceType(GetTypeDefinition<int>());
            bool result = intEnumerable.IsGenericInstanceOf(
                genericEnumerable, 
                typeof(int).GetTypeDefinition());
            Assert.IsTrue(result);
        }

        [Test]
        public void IsGenericInstanceOf_Returns_False_For_IEnumerableInt_Generic_IEnumerable_And_Boolean()
        {
            var genericEnumerable = typeof(IEnumerable<>).GetTypeDefinition();
            var intEnumerable = genericEnumerable.MakeGenericInstanceType(GetTypeDefinition<int>());
            bool result = intEnumerable.IsGenericInstanceOf(
                genericEnumerable,
                typeof(bool).GetTypeDefinition());
            Assert.IsFalse(result);
        }

        [Test]
        public void GetGenericArgumentsFor_List_Of_Int_And_IEnumerable_Of_T_Returns_Int()
        {
            var type = typeof(List<int>).GetTypeReference();
            var result = type.GetGenericArgumentsFor(typeof(IEnumerable<>));

            Assert.AreEqual(1, result.Count());
            Assert.That(result.First().IsEqual(typeof(int)));
        }

        [Test]
        public void GetGenericArgumentsFor_List_Of_Int_And_IComparable_Of_T_Returns_Empty_Result()
        {
            var type = typeof(List<int>).GetTypeReference();
            var result = type.GetGenericArgumentsFor(typeof(IComparable<>));
            Assert.That(result.IsEmpty());
        }

        private static TypeDefinition GetTypeDefinition<T>()
        {
            return typeof(T).GetTypeDefinition();
        }
    }
}
